package cn.b.sky.flowable.service.impl;

import cn.b.sky.flowable.model.condition.ProcessDefinitionCondition;
import cn.b.sky.common.exception.ImeException;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.common.api.FlowableException;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.*;
import org.flowable.engine.runtime.ProcessInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.zip.ZipInputStream;

/**
 * 流程引擎基类
 * 
 * @author lidapeng
 * 
 */
@Service("skyProcessEngine")
public class SkyProcessEngine {

  private static final Logger log = LoggerFactory.getLogger(SkyProcessEngine.class);

  @Autowired
  private RuntimeService runtskyService;

  @Autowired
  private TaskService taskService;

  @Autowired
  private RepositoryService repositoryService;

  @Autowired
  private IdentityService identityService;

  @Autowired
  private HistoryService historyService;

  /**
   * 发布流程
   */
  public Deployment deployProcess(ZipInputStream stream, String name) {
    if (!StringUtils.isBlank(name)) {
      return repositoryService.createDeployment().name(name).addZipInputStream(stream).deploy();
    }
    return null;
  }

  /**
   * <strong>Title : convertProcessDefinitionToModel<br>
   * </strong> <strong>Description : </strong>流程发布包转换为BPMNModel<br>
   * <strong>Create on : 2014-10-15下午2:57:45<br>
   * </strong>
   * <p>
   * 
   * @param deploymentId
   * @return
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public void convertProcessDefinitionToModel(String deploymentId, String category) {
    if (deploymentId == null) {
      return;
    }
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId)
        .singleResult();
    try {
      InputStream bpmnStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
      XMLInputFactory xif = XMLInputFactory.newInstance();
      InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
      XMLStreamReader xtr = xif.createXMLStreamReader(in);
      BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

      if (bpmnModel.getMainProcess() == null || bpmnModel.getMainProcess().getId() == null) {
        log.warn("流程id:" + processDefinition.getId() + "生成流程模型失败,确定这文件包含有效的BPMN");
      } else {

        if (bpmnModel.getLocationMap().size() == 0) {
          log.warn("流程id:" + processDefinition.getId() + "生成流程模型失败,文件中没有找到BMMN DI");
        } else {

         /* Model modelData = repositoryService.newModel();

          ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
          modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
          modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());

          modelData.setCategory(processDefinition.getName());

          modelData.setKey(processDefinition.getKey());
          List<Model> models = repositoryService.createModelQuery().modelKey(processDefinition.getKey())
              .orderByModelVersion().desc().list();
          if (models != null && models.size() > 0) {
            Integer version = models.get(0).getVersion() + 1;
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, models.get(0).getVersion() + 1);
            modelData.setName(processDefinition.getName() + "[" + version + "]");
          } else {
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            modelData.setName(processDefinition.getName() + "[1]");
          }
          modelData.setCategory(category);
          modelData.setMetaInfo(modelObjectNode.toString());
          repositoryService.saveModel(modelData);

          repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));*/
        }
      }
    } catch (Exception e) {
      log.error("流程id:" + processDefinition.getId() + "生成流程模型失败", e);
    }
  }

  /**
   * <strong>Title : startProcess<br>
   * </strong> <strong>Description : </strong>通过流程key启动流程<br>
   * <strong>Create on : 2014-10-9上午9:08:21<br>
   * </strong>
   * <p>
   * 
   * @param variables
   * @param processKey
   * @return ProcessInstance
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public ProcessInstance startProcess(Map<String, Object> variables, String processKey) {

    return runtskyService.startProcessInstanceByKey(processKey, variables);
  }

  public ProcessInstance startProcessByKeyAndTenantId(Map<String, Object> variables, String processKey, String tenantId) {

    try {
      return runtskyService.startProcessInstanceByKeyAndTenantId(processKey, variables, tenantId);
    } catch (FlowableException e) {
      if (e.getMessage().indexOf("no processes deployed with key") != -1) {
        log.warn("没有部署流程!", e);
        throw ImeException.newException("noWorkFlowKey.C", processKey.split(","));
      } else if (e.getCause().getCause().getCause() instanceof ImeException) {
        ImeException ie = (ImeException) e.getCause().getCause().getCause();
        throw ie;
      }
      return null;
    }
  }

  /**
   * <strong>Title : getProcessDefinitionList<br>
   * </strong> <strong>Description : </strong>根据条件获取已经部署的流程<br>
   * <strong>Create on : 2014-10-7下午2:22:55<br>
   * </strong>
   * <p>
   * 
   * @param query
   * @param islast
   * @return List<ProcessDefinition>
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public List<ProcessDefinition> getProcessDefinitionList(ProcessDefinitionCondition query, Boolean islast) {
    ProcessDefinitionQuery pquery = repositoryService.createProcessDefinitionQuery();
    //添加租户概念
    pquery.processDefinitionTenantIdLike(query.getTenantId());
    if (!StringUtils.isBlank(query.getCategory())) {
      pquery = pquery.processDefinitionCategoryLike(query.getCategory());
    }
    if (!StringUtils.isBlank(query.getName())) {
      pquery = pquery.processDefinitionNameLike(query.getName());
    }
    if (!StringUtils.isBlank(query.getKey())) {
      pquery = pquery.processDefinitionKey(query.getKey());
    }
    if (islast != null) {
      pquery = pquery.latestVersion();
    }
    // 按照类别排序
    pquery.orderByProcessDefinitionId().desc();
    return pquery.listPage(query.getStartRow() - 1, query.getEndRow() - 1);
  }

  /**
   * 
   * <strong>Title : getProcessDefinitionsCount<br></strong>
   * <strong>Description : </strong>获取流程定义列表<br> 
   * <strong>Create on : 2015-12-3 下午1:48:57<br></strong>
   * <p>
   * @param query
   * @param islast
   * @return
   * long
   * @throws 
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   */
  public long getProcessDefinitionsCount(ProcessDefinitionCondition query, Boolean islast) {
    ProcessDefinitionQuery pquery = repositoryService.createProcessDefinitionQuery();
    //添加租户概念
    pquery.processDefinitionTenantIdLike(query.getTenantId());
    if (!StringUtils.isBlank(query.getCategory())) {
      pquery = pquery.processDefinitionCategoryLike(query.getCategory());
    }
    if (!StringUtils.isBlank(query.getName())) {
      pquery = pquery.processDefinitionNameLike(query.getName());
    }
    if (!StringUtils.isBlank(query.getKey())) {
      pquery = pquery.processDefinitionKey(query.getKey());
    }
    if (islast != null) {
      pquery = pquery.latestVersion();
    }
    return pquery.count();
  }

  /**
   * <strong>Title : getProcessDefinitionById<br>
   * </strong> <strong>Description : </strong>(通过id获取ProcessDefinition)<br>
   * <strong>Create on : 2014-10-8上午8:42:24<br>
   * </strong>
   * <p>
   * 
   * @param id
   * @return ProcessDefinition
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public ProcessDefinition getProcessDefinitionById(String id) {

    if (StringUtils.isBlank(id)) {
      return null;
    }
    ProcessDefinitionQuery pquery = repositoryService.createProcessDefinitionQuery();
    pquery = pquery.processDefinitionId(id);
    return pquery.singleResult();
  }

  /**
   * 
   * <strong>Title : getProcessDefinitionByDmentId<br>
   * </strong> <strong>Description : </strong>通过发布流程的id查询流程定义<br>
   * <strong>Create on : 2015-11-4 下午5:09:08<br>
   * </strong>
   * <p>
   * 
   * @param deploymentId
   * @return List<ProcessDefinition>
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public List<ProcessDefinition> getProcessDefinitionByDmentId(String deploymentId) {

    if (StringUtils.isBlank(deploymentId)) {
      return null;
    }
    return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).list();
  }

  /**
   * <strong>Title : getProcessDefinitionImg<br>
   * </strong> <strong>Description : </strong>流程图获取<br>
   * <strong>Create on : 2014-10-9上午8:28:23<br>
   * </strong>
   * <p>
   * 
   * @param deploymentId
   * @return InputStream
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public InputStream getProcessDefinitionImg(String deploymentId, String diagramResourceName) {

    if (StringUtils.isBlank(deploymentId)) {
      return null;
    }

    return repositoryService.getResourceAsStream(deploymentId, diagramResourceName);
  }

  /**
   * <strong>Title : getDeployMentById<br>
   * </strong> <strong>Description : </strong>(通过id获取流程发布)<br>
   * <strong>Create on : 2014-10-9上午8:29:00<br>
   * </strong>
   * <p>
   * 
   * @param deploymentId
   * @return Deployment
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public Deployment getDeployMentById(String deploymentId) {
    if (StringUtils.isBlank(deploymentId)) {
      return null;
    }
    return repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
  }

  /**
   * 
   * <strong>Title : getProcessInstanceById<br>
   * </strong> <strong>Description : </strong>获取流程实例，运行中的流程<br>
   * <strong>Create on : 2015-11-3 下午3:21:49<br>
   * </strong>
   * <p>
   * 
   * @param processInstanceId
   * @return ProcessInstance
   * @throws
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   *          <strong>修改历史:</strong><br>
   *          修改人 | 修改日期 | 修改描述<br>
   *          -------------------------------------------<br>
   * <br>
   * <br>
   */
  public ProcessInstance getProcessInstanceById(String processInstanceId) {
    if (!StringUtils.isBlank(processInstanceId)) {
      return runtskyService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    } else {
      return null;
    }
  }

  public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {
    if (!StringUtils.isBlank(processInstanceId)) {
      return historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    } else {
      return null;
    }
  }
}
